package org.wheel.plugins.toolkit.sql.capability;

import org.wheel.plugins.toolkit.sql.core.AbstractWrapper;
import org.wheel.plugins.toolkit.sql.enums.SqlFunction;
import org.wheel.plugins.toolkit.sql.function.FunctionBuilder;
import org.wheel.plugins.toolkit.sql.helper.LambdaUtils;
import org.wheel.plugins.toolkit.sql.helper.SFunction;
import org.wheel.plugins.toolkit.sql.segment.SqlWindowUnit;

import java.util.function.Consumer;

/**
 * 窗口函数能力
 * @param <W> 子类型
 * @param <T> 主实体
 */
public interface LambdaWindow<W extends LambdaWindow<W,T>, T> {

    /* ================== 排名函数 ================== */

    default <D, F> W rowNumber(Consumer<SqlWindowUnit> overConsumer, SFunction<D, F> columnAlias){
        String colAlias = LambdaUtils.getFieldName(columnAlias);
        return rowNumber(overConsumer,  colAlias);
    }
    default W rowNumber(Consumer<SqlWindowUnit> overConsumer, String columnAlias){
        return function(win(SqlFunction.ROW_NUMBER.name(), columnAlias).over(overConsumer));
    }
    default W rowNumber(String columnAlias){
        return function(win(SqlFunction.ROW_NUMBER.name(), columnAlias));
    }
    default W rowNumber(){
        return rowNumber((String)null);
    }

    default <D, F> W rank(Consumer<SqlWindowUnit> overConsumer, SFunction<D, F> columnAlias){
        String colAlias = LambdaUtils.getFieldName(columnAlias);
        return rank(overConsumer, colAlias);
    }
    default <D, F> W rank(Consumer<SqlWindowUnit> overConsumer, String columnAlias){
        return function(win(SqlFunction.RANK.name(), columnAlias).over(overConsumer));
    }
    default W rank(String columnAlias){
        return function(win(SqlFunction.RANK.name(), columnAlias));
    }
    default W rank(){
        return rank((String)null);
    }

    default <D, F> W denseRank(Consumer<SqlWindowUnit> overConsumer, SFunction<D, F> columnAlias){
        String colAlias = LambdaUtils.getFieldName(columnAlias);
        return denseRank(overConsumer, colAlias);
    }
    default <D, F> W denseRank(Consumer<SqlWindowUnit> overConsumer, String columnAlias){
        return function(win(SqlFunction.DENSE_RANK.name(), columnAlias).over(overConsumer));
    }
    default W denseRank(String columnAlias){
        return function(win(SqlFunction.DENSE_RANK.name(), columnAlias));
    }
    default W denseRank(){ return denseRank(null); }

    /* ================== 分桶函数 ================== */
    default <D, F> W ntile(int n, Consumer<SqlWindowUnit> overConsumer, SFunction<D, F> columnAlias){
        String colAlias = LambdaUtils.getFieldName(columnAlias);
        return ntile(n, overConsumer, colAlias);
    }
    default <D, F> W ntile(int n, Consumer<SqlWindowUnit> overConsumer, String columnAlias){
        return function(win(SqlFunction.NTILE.name(), columnAlias).over(overConsumer).arg(n));
    }
    default W ntile(int n, String columnAlias){
        return function(win(SqlFunction.NTILE.name(), columnAlias).arg(n));
    }
    default W ntile(int n){
        return ntile(n, null);
    }

    /* ================== 偏移函数 ================== */
    default <D, F, E, H> W lag(SFunction<D, F> col, int offset, int defaultValue, Consumer<SqlWindowUnit> overConsumer, SFunction<E, H> columnAlias){
        String colAlias = LambdaUtils.getFieldName(columnAlias);
        return function(win(SqlFunction.LAG.name(), colAlias).col(col).arg(offset, defaultValue).over(overConsumer));
    }
    default <D, F> W lag(SFunction<D, F> col, int offset, int defaultValue, Consumer<SqlWindowUnit> overConsumer, String columnAlias){
        return function(win(SqlFunction.LAG.name(), columnAlias).col(col).arg(offset, defaultValue).over(overConsumer));
    }
    default <D, F, E, H> W lag(SFunction<D, F> col, int offset, Consumer<SqlWindowUnit> overConsumer, SFunction<E, H> columnAlias){
        String colAlias = LambdaUtils.getFieldName(columnAlias);
        return function(win(SqlFunction.LAG.name(), colAlias).col(col).arg(offset).over(overConsumer));
    }
    default <D, F> W lag(SFunction<D, F> col, int offset, Consumer<SqlWindowUnit> overConsumer, String columnAlias){
        return function(win(SqlFunction.LAG.name(), columnAlias).col(col).arg(offset).over(overConsumer));
    }
    default W lag(SFunction<T,?> col, int offset, String columnAlias){
        return function(win(SqlFunction.LAG.name(), columnAlias).col(col).arg(offset));
    }
    default W lag(SFunction<T,?> col, int offset){
        return lag(col, offset, null);
    }

    default <D, F, E, H> W lead(SFunction<D, F> col, int offset, int defaultValue, Consumer<SqlWindowUnit> overConsumer, SFunction<E, H> columnAlias){
        String colAlias = LambdaUtils.getFieldName(columnAlias);
        return function(win(SqlFunction.LEAD.name(), colAlias).col(col).arg(offset, defaultValue).over(overConsumer));
    }
    default <D, F> W lead(SFunction<D, F> col, int offset, int defaultValue, Consumer<SqlWindowUnit> overConsumer, String columnAlias){
        return function(win(SqlFunction.LEAD.name(), columnAlias).col(col).arg(offset, defaultValue).over(overConsumer));
    }
    default <D, F, E, H> W lead(SFunction<D, F> col, int offset, Consumer<SqlWindowUnit> overConsumer, SFunction<E, H> columnAlias){
        String colAlias = LambdaUtils.getFieldName(columnAlias);
        return function(win(SqlFunction.LEAD.name(), colAlias).col(col).arg(offset).over(overConsumer));
    }
    default <D, F> W lead(SFunction<D, F> col, int offset, Consumer<SqlWindowUnit> overConsumer, String columnAlias){
        return function(win(SqlFunction.LEAD.name(), columnAlias).col(col).arg(offset).over(overConsumer));
    }
    default <D, E> W lead(SFunction<D,?> col, int offset, SFunction<E, ?> columnAlias){
        String colAlias = LambdaUtils.getFieldName(columnAlias);
        return lead(col, offset, colAlias);
    }
    default <D> W lead(SFunction<D,?> col, int offset, String columnAlias){
        return function(win(SqlFunction.LEAD.name(), columnAlias).col(col).arg(offset));
    }
    default <D> W lead(SFunction<D,?> col, int offset){
        return lead(col, offset, col);
    }

    /* ================== 聚合窗口函数 ================== */
    default <D, E> W sum(SFunction<D,?> col, Consumer<SqlWindowUnit> overConsumer, SFunction<E, ?> columnAlias){
        String colAlias = LambdaUtils.getFieldName(columnAlias);
        return sum(col, overConsumer, colAlias);
    }
    default <D> W sum(SFunction<D,?> col, Consumer<SqlWindowUnit> overConsumer, String columnAlias){
        return function(win(SqlFunction.SUM.name(), columnAlias).col(col).over(overConsumer));
    }

    default <D, E> W count(SFunction<D,?> col, Consumer<SqlWindowUnit> overConsumer, SFunction<E,?> columnAlias){
        String colAlias = LambdaUtils.getFieldName(columnAlias);
        return count(col, overConsumer, colAlias);
    }
    default <D> W count(SFunction<D,?> col, Consumer<SqlWindowUnit> overConsumer, String columnAlias){
        return function(win(SqlFunction.COUNT.name(), columnAlias).col(LambdaUtils.getColumn(col)).over(overConsumer));
    }

    default <D, E> W avg(SFunction<D,?> col, Consumer<SqlWindowUnit> overConsumer, SFunction<E,?> columnAlias){
        String colAlias = LambdaUtils.getFieldName(columnAlias);
        return avg(col, overConsumer, colAlias);
    }
    default <D> W avg(SFunction<D,?> col, Consumer<SqlWindowUnit> overConsumer, String columnAlias){
        return function(win(SqlFunction.AVG.name(), columnAlias).col(LambdaUtils.getColumn(col)).over(overConsumer));
    }

    /* ================== 通用窗口工厂 ================== */
    private FunctionBuilder win(String pluginKey, String columnAlias){
        return FunctionBuilder.plugin(pluginKey, null)
                .as(columnAlias)
                .bind((AbstractWrapper<?, ?>) this);
    }

    /* ================== 原子方法 ================== */
    W function(FunctionBuilder fn);
}